import random

MUTATE_PROB = 0.01

def mutate_layer(size):
    result = int(round(random.gauss(size, size * MUTATE_PROB)))
    if result < 2:
        return 1
    else:
        return result

def mutate_layers(layers):
    """Computes a new layers' list, hidden laye<rs mutated."""
    result = []
    l = 0
    r = 2 * MUTATE_PROB
    while l < len(layers) - 1:
        if r < MUTATE_PROB:
            # insert another layer
            result.append(mutate_layer(layers[l]))

            if len(layers) > l + 1:
                s2 = layers[l+1]
            else:
                s2 = layers[l]
            result.append(mutate_layer((layers[l] + s2)/2))

        elif r >= 2* MUTATE_PROB:
            result.append(mutate_layer(layers[l]))
        # if MUTATE_PROB < r < 2*MUTATE_PROB, skip layer

        r = random.random()
        l += 1

    result.append(layers[-1])

    return result

class SpecialEntry(object):
    def __init__(self, population):
        self.pop = population
        self.value = 0
        self.tries = 1


class GeneratorEntry(SpecialEntry):
    def __init__(self, pop, layers):
        SpecialEntry.__init__(self,pop)
        self.layers = layers

    def createEntry(self):
        newlayers = mutate_layers(self.layers)
        # newEntry is defined via setup
        entry = newEntry(newlayers)
        # TODO: expose initialization of alength as possible parameter
        entry.adaption_length = 0.5
        if DOMAIN.quicktest(entry) < pop.average:
            return entry
        else:
            return None

def average(v1, v2):
    """linear combine & average two vectors."""
    result = copy.deepcopy(v1)
    for i in xrange(len(v1)):
        v1[i] += v2[i]
        v1 = fscalar_prod(0.5, v1)

    return result

combine = average


class MatingEntry(SpecialEntry):
    def getEntry(self):
        candidate = self.pop.select(True)

        partner = self.pop.findMate(entry)
        entry = combine(candidate, partner)
        entry.parent = self
        if DOMAIN.quicktest(entry) < pop.average:
            return entry
        else:
            return None

